﻿using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Urs.Core;
using Urs.Data.Domain.Stores;
using Urs.Data.Domain.Configuration;
using Plugin.Api.Extensions;
using Plugin.Api.Models.Catalog;
using Plugin.Api.Models.Media;
using Plugin.Api.Models.Goods;
using Plugin.Api.Models.Search;
using Urs.Services.Stores;
using Urs.Services.Users;
using Urs.Services.Localization;
using Urs.Services.Media;
using Urs.Services.Security;
using Urs.Framework.Controllers;

namespace Plugin.Api.Controllers
{
    /// <summary>
    /// 搜索接口
    /// </summary>
    [ApiVersion("1.0")]
    [Route("api/v{version:apiVersion}/search")]
    [ApiController]
    public class SearchController : BaseApiController
    {
        private readonly ILocalizationService _localizationService;
        private readonly IWorkContext _workContext;
        private readonly MediaSettings _mediaSettings;
        private readonly ICategoryService _categoryService;
        private readonly IPictureService _pictureService;
        private readonly IWebHelper _webHelper;
        private readonly StoreSettings _storeSettings;
        private readonly IGoodsParameterService _goodsParameterService;
        private readonly IGoodsService _goodsService;
        private readonly CommonSettings _commonSettings;
        private readonly IGoodsTagService _goodsTagService;
        private readonly IUserService _userService;
        private readonly IBrandService _brandService;

        /// <summary>
        /// 构造器
        /// </summary>
        public SearchController(ILocalizationService localizationService,
            IWorkContext workContext,
            MediaSettings mediaSettings,
            ICategoryService categoryService,
            IPictureService pictureService,
            IWebHelper webHelper,
            StoreSettings storeSettings,
            IGoodsParameterService goodsParameterService,
            IGoodsService goodsService,
            IPermissionService permissionService,
            CommonSettings commonSettings,
            IGoodsTagService goodsTagService,
            IUserService userService,
            IBrandService brandService)
        {
            this._localizationService = localizationService;
            this._workContext = workContext;
            this._mediaSettings = mediaSettings;
            this._categoryService = categoryService;
            this._pictureService = pictureService;
            this._webHelper = webHelper;
            this._storeSettings = storeSettings;
            this._goodsParameterService = goodsParameterService;
            this._goodsService = goodsService;
            this._commonSettings = commonSettings;
            this._goodsTagService = goodsTagService;
            this._userService = userService;
            this._brandService = brandService;
        }

        #region Utilities
        [NonAction]
        protected List<int> GetChildCategoryIds(int parentCategoryId, bool showHidden = false)
        {
            var userRolesIds = _workContext.CurrentUser.UserRoles
                .Where(cr => cr.Active).Select(cr => cr.Id).ToList();
            
                var categoriesIds = new List<int>();
                var categories = _categoryService.GetAllCategoriesByParentCategoryId(parentCategoryId, showHidden);
                foreach (var category in categories)
                {
                    categoriesIds.Add(category.Id);
                    categoriesIds.AddRange(GetChildCategoryIds(category.Id, showHidden));
                }
                return categoriesIds;
        }

        [NonAction]
        protected IEnumerable<MoGoodsOverview> PrepareGoodsOverviewModels(IEnumerable<Goods> list,
            bool preparePriceModel = true, bool preparePictureModel = true,
            int? goodsThumbPictureSize = null, bool prepareGoodsParameters = false, bool prepareGoodsTag = false,
            bool forceRedirectionAfterAddingToCart = false, bool prepareFullDesc = false)
        {
            if (list == null)
                throw new ArgumentNullException("list");

            var models = new List<MoGoodsOverview>();
            foreach (var goods in list)
            {
                var model = new MoGoodsOverview()
                {
                    Id = goods.Id,
                    Sku = goods.Sku,
                    Name = goods.Name,
                    Short = goods.ShortDescription,
                    Qty = goods.StockQuantity,
                    SaleVolume = goods.SaleVolume
                };
                if (prepareFullDesc)
                    model.Full = goods.FullDescription;
                //price
                if (preparePriceModel)
                {
                    #region Prepare goods price

                    var priceModel = new MoGoodsOverview.MoPrice();

                    priceModel.OldPrice = PriceFormatter.FormatPrice(goods.OldPrice);
                    priceModel.Price = PriceFormatter.FormatPrice(goods.Price);
                    priceModel.ProductCost = PriceFormatter.FormatPrice(goods.ProductCost);
                    model.Price = priceModel;
                    #endregion
                }

                //picture
                if (preparePictureModel)
                {
                    #region Prepare goods picture

                    //If a size has been set in the view, we use it in priority
                    int pictureSize = goodsThumbPictureSize.HasValue ? goodsThumbPictureSize.Value : _mediaSettings.GoodsThumbPictureSize;
                   
                        var picture = goods.GetDefaultGoodsPicture(_pictureService);
                        var pictureModel = new MoPicture()
                        {
                            NormalUrl = _pictureService.GetPictureUrl(picture, pictureSize),
                            BigUrl = _pictureService.GetPictureUrl(picture)
                        };
                        model.Picture = pictureModel;
                    
                    #endregion
                }

                //specs
                if (prepareGoodsParameters)
                {
                    //specs for comparing
                    model.Specs = PrepareGoodsSpecificationModel(goods);
                }

                if (prepareGoodsTag)
                {
                    for (int i = 0; i < goods.GoodsTags.Count; i++)
                    {
                        var pt = goods.GoodsTags.ToList()[i];
                        model.Tags.Add(new MoGoodsOverview.MoGoodsTag()
                        {
                            Id = pt.GoodsTag.Id,
                            Name = pt.GoodsTag.Name
                        });
                    }
                }
                models.Add(model);
            }
            return models;
        }

        [NonAction]
        protected IList<MoSpecification> PrepareGoodsSpecificationModel(Goods goods)
        {
            if (goods == null)
                throw new ArgumentNullException("goods");

                var model = _goodsParameterService.GetGoodsParameterMappingByGoodsId(goods.Id, null, true)
                   .Select(psa =>
                   {
                       return new MoSpecification()
                       {
                           Name = psa.GoodsParameterOption.Mapping.Name,
                           Value = !String.IsNullOrEmpty(psa.CustomValue) ? psa.CustomValue : psa.GoodsParameterOption.Name,
                       };
                   }).ToList();
                return model;
        }

        protected MoPicture PrepareAvatar(int picId, int pictureSize)
        {

            var picture = _pictureService.GetPictureById(picId);
            var pictureModel = new MoPicture()
            {
                NormalUrl = _pictureService.GetPictureUrl(picture, pictureSize),
                BigUrl = _pictureService.GetPictureUrl(picture)
            };
            return pictureModel;
        }

        #endregion

        #region Searching

        /// <summary>
        /// 按关键词搜索商品
        /// </summary>
        /// <param name="q">查询关键字</param>
        /// <param name="sid">是否搜索描述;否</param>
        /// <param name="cid">分类Id(默认:0)</param>
        /// <param name="mid">品牌Id(默认:0)</param>
        /// <param name="minPrice">最小价格(默认:空)</param>
        /// <param name="maxPrice">最大价格(默认:空)</param>
        /// <param name="page">页码（默认：1）</param>
        /// <param name="size">页大小（默认值：12）</param>
        /// <param name="orderBy">排序（默认:综合 0|名称升序:5|名称降序:6|销量升序:20|销量降序:21)</param>
        /// <param name="specshow">显示参数</param>
        /// <param name="tagshow">显示标签</param>
        /// <returns></returns>
        [HttpGet("find")]
        public async Task<ApiResponse<MoSearch>> Find(string q, bool sid = false, int mid = 0, int cid = 0,
            decimal? minPrice = null, decimal? maxPrice = null,
            int page = 1, int size = 12, int orderBy = (int)GoodsSortingEnum.Position,
            bool specshow = false, bool tagshow = false)
        {
            if (!string.IsNullOrEmpty(q) && q.Length < _storeSettings.GoodsSearchTermMinimumLength)
            {
                var warn = string.Format(_localizationService.GetResource("Search.SearchTermMinimumLengthIsNCharacters"), _storeSettings.GoodsSearchTermMinimumLength);
                return ApiResponse<MoSearch>.Warn(warn);
            }
            var data = await Task.Run(() =>
            {
                var model = new MoSearch();
                var command = new MoPagingFiltering();
                command.PageNumber = page > 0 ? page : 0;
                command.PageSize = size > 0 ? size : _storeSettings.SearchPageGoodssPerPage;
                command.OrderBy = orderBy;
                model.Q = q;
                model.Sid = sid;
                model.Mid = mid;

                var categoryIds = new List<int>();
                if (cid > 0)
                    categoryIds.Add(cid);
                //list
                var list = _goodsService.SearchGoodss(out IList<int> filterableGoodsParameterOptionIds,
                    categoryId: categoryIds.ToArray(), brandId: mid,
                    priceMin: minPrice, priceMax: maxPrice, keywords: model.Q, searchDescriptions: model.Sid,
                    orderBy: (GoodsSortingEnum)orderBy, pageIndex: page - 1, pageSize: size);

                model.Items = PrepareGoodsOverviewModels(list,
                    prepareGoodsParameters: specshow, prepareGoodsTag: tagshow,
                    prepareFullDesc: _storeSettings.GoodsSearchFullDescEnabled).ToList();
                model.Paging.Page = page;
                model.Paging.Size = size;
                model.Paging.TotalPage = list.TotalPages;
                return model;
            });
            return ApiResponse<MoSearch>.Success(data);
        }

        /// <summary>
        /// 按分类搜索商品
        /// </summary>
        /// <param name="cid">分类Id</param>
        /// <param name="mid">品牌Id(默认:0)</param>
        /// <param name="tagName">标签名称(默认:空)</param>
        /// <param name="minPrice">最小价格(默认:空)</param>
        /// <param name="maxPrice">最大价格(默认:空)</param>
        /// <param name="page">页码（默认：1）</param>
        /// <param name="size">页大小（默认值：12）</param>
        /// <param name="orderBy">排序（默认:综合 0|名称升序:5|名称降序:6|销量升序:20|销量降序:21)</param>
        /// <param name="specshow">显示参数</param>
        /// <param name="tagshow">显示标签</param>
        /// <returns></returns>
        [HttpGet("findbycid")]
        public async Task<ApiResponse<MoCategory>> FindByCid(int cid, int mid = 0, string tagName = "",
            decimal? minPrice = null, decimal? maxPrice = null,
            int page = 1, int size = 12, int orderBy = (int)GoodsSortingEnum.Position,
            bool specshow = false, bool tagshow = false)
        {
            var category = _categoryService.GetCategoryById(cid) ?? new Category();

            var data = await Task.Run(() =>
            {
                var model = category.ToModel();
                var command = new MoPagingFiltering();
                command.PageNumber = page > 0 ? page : 0;
                command.PageSize = size > 0 ? size : (category.PageSize > 0 ? category.PageSize : _storeSettings.SearchPageGoodssPerPage);
                command.OrderBy = orderBy;

                var tagId = _goodsTagService.GetGoodsTagByName(tagName)?.Id;

                var categoryIds = new List<int>();
                if (category.Id > 0)
                    categoryIds.Add(category.Id);
                //list
                var list = _goodsService.SearchGoodss(out IList<int> filterableGoodsParameterOptionIds,
                    categoryId: categoryIds.ToArray(), brandId: mid,
                    featuredGoodss: _storeSettings.IncludeFeaturedGoodssInNormalLists ? null : (bool?)false,
                    priceMin: minPrice, priceMax: maxPrice, goodsTagId: tagId ?? 0, orderBy: (GoodsSortingEnum)command.OrderBy,
                    pageIndex: command.PageNumber - 1, pageSize: command.PageSize);

                model.Items = PrepareGoodsOverviewModels(list,
                    prepareGoodsParameters: specshow, prepareGoodsTag: tagshow,
                    prepareFullDesc: _storeSettings.GoodsSearchFullDescEnabled).ToList();
                model.Paging.Page = page;
                model.Paging.Size = size;
                model.Paging.TotalPage = list.TotalPages;
                return model;
            });
            return ApiResponse<MoCategory>.Success(data);
        }
        /// <summary>
        /// 按品牌搜索商品
        /// </summary>
        /// <param name="mid">品牌Id</param>
        /// <param name="cid">分类Id(默认:0)</param>
        /// <param name="tagName">标签名称(默认:空)</param>
        /// <param name="minPrice">最小价格(默认:空)</param>
        /// <param name="maxPrice">最大价格(默认:空)</param>
        /// <param name="page">页码（默认：1）</param>
        /// <param name="size">页大小（默认值：12）</param>
        /// <param name="orderBy">排序（默认:综合 0|名称升序:5|名称降序:6|销量升序:20|销量降序:21)</param>
        /// <param name="specshow">显示参数</param>
        /// <param name="tagshow">显示标签</param>
        /// <returns></returns>
        [HttpGet("findbymid")]
        public async Task<ApiResponse<MoBrand>> FindByMid(int mid = 0, int cid = 0, string tagName = "",
            decimal? minPrice = null, decimal? maxPrice = null,
            int page = 1, int size = 12, int orderBy = (int)GoodsSortingEnum.Position,
            bool specshow = false, bool tagshow = false)
        {
            var mfr = _brandService.GetById(mid);
            if (mfr == null || mfr.Deleted || !mfr.Published)
                return ApiResponse<MoBrand>.BadRequest();

            var data = await Task.Run(() =>
            {
                var model = mfr.ToModel();
                var command = new MoPagingFiltering();
                command.PageNumber = page;
                command.PageSize = size;
                command.OrderBy = orderBy;

                var tagId = _goodsTagService.GetGoodsTagByName(tagName)?.Id;

                var categoryIds = new List<int>();
                if (cid > 0)
                    categoryIds.Add(cid);
                //list
                var list = _goodsService.SearchGoodss(out IList<int> filterableGoodsParameterOptionIds,
                    categoryId: categoryIds.ToArray(), brandId: mfr.Id, featuredGoodss: _storeSettings.IncludeFeaturedGoodssInNormalLists ? null : (bool?)false,
                    priceMin: minPrice, priceMax: maxPrice, goodsTagId: tagId ?? 0,
                    orderBy: (GoodsSortingEnum)command.OrderBy, pageIndex: command.PageIndex, pageSize: command.PageSize);

                model.Items = PrepareGoodsOverviewModels(list,
                    prepareGoodsParameters: specshow, prepareGoodsTag: tagshow,
                    prepareFullDesc: _storeSettings.GoodsSearchFullDescEnabled).ToList();
                model.Paging.Page = page;
                model.Paging.Size = size;
                model.Paging.TotalPage = list.TotalPages;
                return model;
            });
            return ApiResponse<MoBrand>.Success(data);
        }
        /// <summary>
        /// 按操作人搜索商品
        /// </summary>
        /// <param name="oid">操作人</param>
        /// <param name="mid">品牌Id(默认:0)</param>
        /// <param name="cid">分类Id(默认:0)</param>
        /// <param name="tagName">标签名称(默认:空)</param>
        /// <param name="minPrice">最小价格(默认:空)</param>
        /// <param name="maxPrice">最大价格(默认:空)</param>
        /// <param name="page">页码（默认：1）</param>
        /// <param name="size">页大小（默认值：12）</param>
        /// <param name="orderBy">排序（默认:综合 0|名称升序:5|名称降序:6|销量升序:20|销量降序:21)</param>
        /// <param name="specshow">显示参数</param>
        /// <param name="tagshow">显示标签</param>
        /// <returns></returns>
        [HttpGet("findbyoid")]
        public async Task<ApiResponse<MoUserSearch>> FindByOId(int oid, int mid = 0, int cid = 0, string tagName = "",
            decimal? minPrice = null, decimal? maxPrice = null,
            int page = 1, int size = 12, int orderBy = (int)GoodsSortingEnum.Position,
            bool specshow = false, bool tagshow = false)
        {
            var user = _userService.GetUserById(oid);
            if (user == null || user.Deleted || !user.Active)
                return ApiResponse<MoUserSearch>.BadRequest();

            var data = await Task.Run(() =>
            {
                var model = user.ToModel();

                var command = new MoPagingFiltering();

                model.Id = user.Id;
                model.Name = user.Nickname;
                command.PageNumber = page;
                command.PageSize = size;
                command.OrderBy = orderBy;

                var tagId = _goodsTagService.GetGoodsTagByName(tagName)?.Id;

                var categoryIds = new List<int>();
                if (cid > 0)
                    categoryIds.Add(cid);
                //list
                var list = _goodsService.SearchGoodss(out IList<int> filterableGoodsParameterOptionIds, categoryId: categoryIds.ToArray(), brandId: mid, creatorId: oid,
                    featuredGoodss: _storeSettings.IncludeFeaturedGoodssInNormalLists ? null : (bool?)false,
                    priceMin: minPrice, priceMax: maxPrice, goodsTagId: tagId ?? 0,
                    orderBy: (GoodsSortingEnum)command.OrderBy, pageIndex: command.PageIndex, pageSize: command.PageSize);

                model.Items = PrepareGoodsOverviewModels(list,
                    prepareGoodsParameters: specshow, prepareGoodsTag: tagshow,
                    prepareFullDesc: _storeSettings.GoodsSearchFullDescEnabled).ToList();
                model.Paging.Page = page;
                model.Paging.Size = size;
                model.Paging.TotalPage = list.TotalPages;
                return model;
            });
            return ApiResponse<MoUserSearch>.Success(data);
        }

        /// <summary>
        /// 热门搜索关键字
        /// </summary>
        /// <returns></returns>
        [HttpGet("hot")]
        public async Task<ApiResponse<List<string>>> HotSearch()
        {
            var list = new List<string>();
            var phrases = _commonSettings.HotSearchPhrases != null ? _commonSettings.HotSearchPhrases.Split(',', '，') : null;
            if (phrases != null)
                foreach (var p in phrases)
                {
                    list.Add(p);
                }
            return ApiResponse<List<string>>.Success(list);
        }
        /// <summary>
        /// 搜索历史
        /// </summary>
        /// <returns>放回数组</returns>
        [HttpGet("history")]
        public async Task<ApiResponse<List<string>>> History()
        {
            var list = new List<string>();
            list.Add("茶");
            list.Add("茶具");
            list.Add("茶杯");
            list.Add("龙井茶");

            return ApiResponse<List<string>>.Success(list);
        }
        #endregion
    }
}
